﻿using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Threading;
using YEA.Utilities.Service.Controller;

namespace YEA.Monitor.LogService.States
{
    public enum Diagnostic { MessageBusUnavailable, DatabaseError, FatalForUnknownReasons, Unknown };

    public class DiagnosticState : ServiceStateBase, IServiceState
    {
        protected Diagnostic diagnosis;
        protected Exception exception;
        protected int counter;
        protected Stopwatch stopwatch;
        protected Dictionary<string, DateTime> errorList;
        protected bool paused;
        private bool stopped;

        public log4net.ILog Logger { get; set; }

        public DiagnosticState()
        {
            errorList = new Dictionary<string, DateTime>();
            diagnosis = Diagnostic.Unknown;
            paused = false;
            stopped = false;

        }

        public void SetException( Exception ex)
        {
            if (exception != null && ExceptionSignature(ex) == ExceptionSignature(exception))
            {
                if (exception.Data.Contains("counter"))
                    exception.Data["counter"] = (int)exception.Data["counter"] + 1;
                else
                    exception.Data.Add("counter", 1);
                exception.Data["time"] = DateTime.Now;
            }
            else
            {
                ex.Data.Add("time", DateTime.Now);
                ex.Data.Add("counter", 1);
                exception = ex;
            }
        }

        private string ExceptionSignature(Exception ex)
        {
            return ex.Message;
        }

        public void Start()
        {
            var tbetweenerrors = MilliSecondsSinceLastError();
            var tbetweensameerror = MilliSecondsSinceSameError();
            //LogErrorInMemory();

            Diagnose(exception);
            while (paused)
            {
                Thread.Sleep(1000);
            }
            if( !stopped )
               
            SendStatusEvent(new StateEventArgs { state = StateEvent.Diagnosed, stateDetails = diagnosis });
        }

        private void LogErrorInMemory()
        {
            string signature = ExceptionSignature(exception);
            if (errorList.ContainsKey(signature))
                errorList[signature] = DateTime.Now;
            else
                errorList.Add( signature, DateTime.Now);
        }

        protected void Diagnose(Exception e)
        {
            if (MessageBusError(e))
            {
                diagnosis = Diagnostic.MessageBusUnavailable;
            }
            else if (DatabaseError(e))
            {
                diagnosis = Diagnostic.DatabaseError;
            }
            else if (errorsInInterval() < 100)
            {
                diagnosis = Diagnostic.Unknown;
            }
            else
            {
                Logger.Fatal("Fatal error occured", exception);
                diagnosis = Diagnostic.FatalForUnknownReasons;
            }
        }

        private int errorsInInterval()
        {
            //Todo: handle this one appropriately.
            return 1;
        }

        private bool DatabaseError(Exception e)
        {
            return false;
        }

        private static bool MessageBusError(Exception e)
        {
            if (e == null)
                return false;
            return e is RabbitMQ.Client.Exceptions.OperationInterruptedException
                            || e is RabbitMQ.Client.Exceptions.BrokerUnreachableException
                            || e is RabbitMQ.Client.Exceptions.AlreadyClosedException
                            || e.Source == "RabbitMQ.Client"
                            || MessageBusError(e.InnerException);
        }

        private long MilliSecondsSinceSameError()
        {
            if (!errorList.ContainsKey(exception.Message))
                return -1;
            return errorList[exception.Message].Subtract(DateTime.Now).Milliseconds;
        }

        private long MilliSecondsSinceLastError()
        {
            if (stopwatch == null)
            {
                stopwatch = new Stopwatch();
                stopwatch.Start();
                return -1;
            }
            else
                return stopwatch.ElapsedMilliseconds;
        }

        public void Stop()
        {
            stopped = true;
        }

        public void Pause()
        {
            paused = true;
        }

        public void Continue()
        {
            paused = false;
        }



        public void Dispose()
        {
            
        }
    }
}
